package com.ds.infrastructure.hyperspace.console.service.impl;

import com.ds.infrastructure.hyperspace.console.config.proxy.ProxyServiceConfiguration;
import com.ds.infrastructure.hyperspace.console.config.shiro.ShiroToken;
import com.ds.infrastructure.hyperspace.console.constants.ConsoleConstant;
import com.ds.infrastructure.hyperspace.console.dto.apirouter.ApiRouterConfigDTO;
import com.ds.infrastructure.hyperspace.console.dto.apirouter.ApiRouterResultDTO;
import com.ds.infrastructure.hyperspace.console.dto.configdata.ContainerDataDTO;
import com.ds.infrastructure.hyperspace.console.entity.enums.*;
import com.ds.infrastructure.hyperspace.console.entity.page.TbPageWhere;
import com.ds.infrastructure.hyperspace.console.entity.table.*;
import com.ds.infrastructure.hyperspace.console.exceptions.HyperspaceException;
import com.ds.infrastructure.hyperspace.console.service.inf.*;
import com.ds.infrastructure.hyperspace.console.utils.FastJsonUtil;
import com.ds.infrastructure.hyperspace.console.utils.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.ContentType;
import org.apache.http.protocol.HTTP;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

import static com.ds.infrastructure.hyperspace.console.constants.ConsoleConstant.API_ROUTER_RESULT_DTO_CODE_SUCCESS;

/**
 * @author: yaozhendong
 * @create: 2019-10-24 10:08
 **/
@Slf4j
@Service
public class ContainerManageService implements IContainerManageService {

    @Autowired
    IContainerConfigService iContainerConfigService;
    @Autowired
    IReleaseTaskService iReleaseTaskService;
    @Autowired
    IReleaseContainerConfigService iReleaseContainerConfigService;
    @Autowired
    IDeployNodeService iDeployNodeService;
    @Autowired
    IReleaseNodeService iReleaseNodeService;
    @Autowired
    IContainerActiveConfigService iContainerActiveConfigService;
    @Autowired
    INodeExecuteLogService iNodeExecuteLogService;
    @Resource
    private ProxyServiceConfiguration proxyServiceConfiguration;
    @Autowired
    private IDeployInstallTaskService iDeployInstallTaskService;
    @Autowired
    private ISSOService iSSOService;

    @Override
    public Long release(Long containerConfigId, Long deployInstallTaskId, List<String> hostGroupIds) {
        TbContainerConfig tbContainerConfig = iContainerConfigService.getOrThrow(containerConfigId);
        TbDeployInstallTask tbDeployInstallTask = iDeployInstallTaskService.getOrThrow(deployInstallTaskId);
        //
        if (!tbContainerConfig.getStageId().equals(tbDeployInstallTask.getStageId())) {
            throw new HyperspaceException("当前网关配置信息与部署任务不在同一个stage下，containerConfigId=" + containerConfigId + ";deployInstallTaskId=" + deployInstallTaskId);
        }
        //
        if (!TbContainerConfigEnum.EDIT_STATE.NO.code.equals(tbContainerConfig.getEditState())) {
            throw new HyperspaceException("当前配置记录没有提交，不可使用到网关，id=" + containerConfigId);
        }
        Long taskId = createReleaseTask(tbContainerConfig, deployInstallTaskId, hostGroupIds);
        createReleaseContainerConfig(taskId, tbContainerConfig);
        createReleaseNode(taskId, deployInstallTaskId, hostGroupIds);
        return taskId;
    }

    @Override
    public void executeReleaseTask(Long taskId) {
        TbReleaseTask tbReleaseTask = iReleaseTaskService.get(taskId);
        if (TbPublicEnum.TASK_COMPLETE_STATE.COMPLETE.value.equals(tbReleaseTask.getCompleteState())) {
            return;
        }
        executeReleaseTaskForReleaseNode(taskId);
        updateStateForReleaseTask(taskId, TbReleaseTaskEnum.EXECUTE_STATE.SUCCESS, TbPublicEnum.TASK_COMPLETE_STATE.COMPLETE);
        updateContainerActiveConfig(tbReleaseTask);
    }

    private void updateContainerActiveConfig(TbReleaseTask tbReleaseTask) {
        int proxyCount = getProxyCount(tbReleaseTask.getContainerConfigId());
        Long containerActiveConfigId = getTbContainerActiveConfigId(tbReleaseTask);
        TbContainerActiveConfig entity = new TbContainerActiveConfig();
        entity.setId(containerActiveConfigId);
        entity.setOnlineConfigId(tbReleaseTask.getContainerConfigId());
        entity.setOnlineReleaseTaskId(tbReleaseTask.getId());
        entity.setProxyCount(proxyCount);
        iContainerActiveConfigService.update(entity);
    }

    private int getProxyCount(Long containerConfigId) {
        TbContainerConfig tbContainerConfig = iContainerConfigService.get(containerConfigId);
        ContainerDataDTO containerDataDTO = FastJsonUtil.deserialize(tbContainerConfig.getConfigData(), ContainerDataDTO.class);
        return containerDataDTO.getContainer().size();
    }

    private Long getTbContainerActiveConfigId(TbReleaseTask tbReleaseTask) {
        TbContainerActiveConfig tbContainerActiveConfig = iContainerActiveConfigService.selectLastOneByStageId(tbReleaseTask.getStageId());
        if (tbContainerActiveConfig != null) {
            return tbContainerActiveConfig.getId();
        }
        TbContainerActiveConfig entity = new TbContainerActiveConfig();
        entity.setStageId(tbReleaseTask.getStageId());
        entity.setEnvironmentId(tbReleaseTask.getEnvironmentId());
        entity.setOnlineConfigId(TbPublicEnum.ID_VALUE.DEFAULT.value);
        entity.setOnlineReleaseTaskId(TbPublicEnum.ID_VALUE.DEFAULT.value);
        entity.setGmtIsDeleted(TbPublicEnum.GMD_IS_DEL.NO.code);
        iContainerActiveConfigService.save(entity);
        return entity.getId();
    }

    private void executeReleaseTaskForReleaseNode(Long taskId) {
        String containerConfig = getReleaseContainerConfigByReleaseTaskId(taskId);
        List<TbReleaseNode> tbReleaseNodeList = getTbReleaseNodesByReleaseTaskId(taskId);
        tbReleaseNodeList.forEach(item -> {
            //
            String requestUrl = proxyServiceConfiguration.getProtocol() + item.getHost() + ":" + proxyServiceConfiguration.getPort() + proxyServiceConfiguration.getDeploy();
            StringBuilder requestData = new StringBuilder();
            requestData.append("NODE:").append(item.getHost()).append(";").append(System.lineSeparator());
            requestData.append("HOST GROUP:").append(item.getHostGroupName()).append(";").append(System.lineSeparator());
            requestData.append("REQUEST URL:").append(requestUrl).append(";").append(System.lineSeparator());
            requestData.append("REQUEST DATA:").append(containerConfig).append(System.lineSeparator());
            log.info(requestData.toString());
            Long nodeExecuteLogId = createNodeExecuteLog(item, requestData.toString());
            //
            TbNodeExecuteLogEnum.EXECUTE_STATE nodeExecuteState = TbNodeExecuteLogEnum.EXECUTE_STATE.FAIL;
            TbReleaseNodeEnum.PUSH_STATE nodePushState = TbReleaseNodeEnum.PUSH_STATE.FAIL;
            String responseBody = null;
            try {
                responseBody = HttpUtil.postRequest(requestUrl)
                        .setHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE)
                        .bodyString(containerConfig, ContentType.APPLICATION_JSON).execute().returnContent().asString();
                log.info(responseBody);
                //解析responseBody
                ApiRouterResultDTO resultDTO = FastJsonUtil.deserialize(responseBody, ApiRouterResultDTO.class);
                if (API_ROUTER_RESULT_DTO_CODE_SUCCESS.equals(resultDTO.getCode())) {
                    nodeExecuteState = TbNodeExecuteLogEnum.EXECUTE_STATE.SUCCESS;
                    nodePushState = TbReleaseNodeEnum.PUSH_STATE.SUCCESS;
                }
            } catch (Exception e) {
                log.error("推送网关配置数据请求失败：", e);
                responseBody = new StringBuilder().append("Exception:").append(e.toString()).append(System.lineSeparator()).append(" ; message:").append(e.getMessage()).toString();
            }
            //
            StringBuilder responseData = new StringBuilder();
            responseData.append(responseBody).append(System.lineSeparator());
            //
            updateNodeExecutedLog(nodeExecuteLogId, responseData.toString(), nodeExecuteState);
            //
            TbReleaseNode tbReleaseNode = new TbReleaseNode();
            tbReleaseNode.setId(item.getId());
            tbReleaseNode.setPushState(nodePushState.code);
            iReleaseNodeService.update(tbReleaseNode);
        });
    }

    private String getReleaseContainerConfigByReleaseTaskId(Long taskId) {
        TbReleaseContainerConfig where = new TbReleaseContainerConfig();
        where.setReleaseTaskId(taskId);
        where.setGmtIsDeleted(TbPublicEnum.GMD_IS_DEL.NO.code);
        TbReleaseContainerConfig tbReleaseContainerConfig = iReleaseContainerConfigService.selectOne(where, null);
        TbReleaseContainerConfig tbReleaseContainerConfigWithBlog = iReleaseContainerConfigService.get(tbReleaseContainerConfig.getId());
        return tbReleaseContainerConfigWithBlog.getContainerConfig();
    }

    private List<TbReleaseNode> getTbReleaseNodesByReleaseTaskId(Long taskId) {
        TbReleaseNode where = new TbReleaseNode();
        where.setReleaseTaskId(taskId);
        where.setGmtIsDeleted(TbPublicEnum.GMD_IS_DEL.NO.code);
        return iReleaseNodeService.getList(where, null, TbPageWhere.newPage4All());
    }

    private void updateNodeExecutedLog(Long nodeExecuteLogId, String responseData, TbNodeExecuteLogEnum.EXECUTE_STATE executeState) {
        TbNodeExecuteLog tbNodeExecuteLog = new TbNodeExecuteLog();
        tbNodeExecuteLog.setId(nodeExecuteLogId);
        tbNodeExecuteLog.setResponseData(responseData);
        tbNodeExecuteLog.setExecuteState(executeState.code);
        iNodeExecuteLogService.update(tbNodeExecuteLog);
    }

    private Long createNodeExecuteLog(TbReleaseNode item, String requestData) {
        TbNodeExecuteLog tbNodeExecuteLog = new TbNodeExecuteLog();
        tbNodeExecuteLog.setNodeId(item.getId());
        tbNodeExecuteLog.setTaskId(item.getReleaseTaskId());
        tbNodeExecuteLog.setTaskType(TbNodeExecuteLogEnum.TASK_TYPE.RELEASE_TASK.code);
        tbNodeExecuteLog.setStepId(TbNodeExecuteLogEnum.RELEASE_TASK_STEP.PUSH_DATA.code);
        tbNodeExecuteLog.setStepName(TbNodeExecuteLogEnum.RELEASE_TASK_STEP.PUSH_DATA.desc);
        tbNodeExecuteLog.setExecuteState(TbNodeExecuteLogEnum.EXECUTE_STATE.INIT.code);
        tbNodeExecuteLog.setRequestData(requestData);
        tbNodeExecuteLog.setGmtIsDeleted(TbPublicEnum.GMD_IS_DEL.NO.code);
        iNodeExecuteLogService.save(tbNodeExecuteLog);
        return tbNodeExecuteLog.getId();

    }

    private void createReleaseNode(Long releaseTaskId, Long deployInstallTaskId, List<String> hostGroupIds) {
        TbDeployNode where = new TbDeployNode();
        where.setDeployTaskId(deployInstallTaskId);
        where.setGmtIsDeleted(TbPublicEnum.GMD_IS_DEL.NO.code);
        List<TbDeployNode> tbDeployNodeList = iDeployNodeService.getList(where, null, TbPageWhere.newPage4All());
        List<TbReleaseNode> tbReleaseNodeList = new ArrayList<>();
        for (TbDeployNode tbDeployNode : tbDeployNodeList) {
            if (!hostGroupIds.contains(tbDeployNode.getHostGroupId())) {
                continue;
            }
            TbReleaseNode item = new TbReleaseNode();
            item.setReleaseTaskId(releaseTaskId);
            item.setActiveTaskId(tbDeployNode.getActiveTaskId());
            item.setHostGroupId(tbDeployNode.getHostGroupId());
            item.setHostGroupName(tbDeployNode.getHostGroupName());
            item.setHost(tbDeployNode.getHost());
            item.setHostType(tbDeployNode.getHostType());
            item.setPushState(TbReleaseNodeEnum.PUSH_STATE.INIT.code);
            item.setGmtIsDeleted(TbPublicEnum.GMD_IS_DEL.NO.code);
            tbReleaseNodeList.add(item);
        }
        if (tbReleaseNodeList.isEmpty()) {
            throw new HyperspaceException("没有可用的发布节点");
        }
        iReleaseNodeService.insertList(tbReleaseNodeList);
        //
        updateStateForReleaseTask(releaseTaskId, TbReleaseTaskEnum.EXECUTE_STATE.CREATE_NODE, null);
    }

    private void createReleaseContainerConfig(Long releaseTaskId, TbContainerConfig tbContainerConfig) {
        ContainerDataDTO containerDataDTO = FastJsonUtil.deserialize(tbContainerConfig.getConfigData(), ContainerDataDTO.class);
        if (containerDataDTO == null) {
            throw new HyperspaceException("没找到对应的网关配置数据");
        }
        if (containerDataDTO.getContainer() == null || containerDataDTO.getContainer().isEmpty()) {
            throw new HyperspaceException("没找到对应的网关配置数据");
        }
        ApiRouterConfigDTO apiRouterConfigDTO = new ApiRouterConfigDTO();
        apiRouterConfigDTO.setVersion(tbContainerConfig.getConfigVersion());
        containerDataDTO.getContainer().forEach(item -> ApiRouterConfigDTO.toApiRouter(item, apiRouterConfigDTO));
        //
        TbReleaseContainerConfig tbReleaseContainerConfig = new TbReleaseContainerConfig();
        tbReleaseContainerConfig.setReleaseTaskId(releaseTaskId);
        tbReleaseContainerConfig.setContainerConfig(FastJsonUtil.serialize(apiRouterConfigDTO));
        tbReleaseContainerConfig.setGmtIsDeleted(TbPublicEnum.GMD_IS_DEL.NO.code);
        /**
         //验证目前定义的格式是否正确
         //String checkJson=FastJsonUtil.serialize(apiRouterConfigDTO);
         //ContainerConfig checkObj=FastJsonUtil.deserialize(checkJson,ContainerConfig.class);
         */
        iReleaseContainerConfigService.save(tbReleaseContainerConfig);
        //
        updateStateForReleaseTask(releaseTaskId, TbReleaseTaskEnum.EXECUTE_STATE.CREATE_CONFIG, null);
    }

    private void updateStateForReleaseTask(Long taskId, TbReleaseTaskEnum.EXECUTE_STATE executeState, TbPublicEnum.TASK_COMPLETE_STATE completeState) {
        if (executeState == null && completeState == null) {
            throw new HyperspaceException("executeState==null&&completeState==null");
        }
        TbReleaseTask tbReleaseTask = new TbReleaseTask();
        tbReleaseTask.setId(taskId);
        if (executeState != null) {
            tbReleaseTask.setExecuteState(executeState.code);
        }
        if (completeState != null) {
            tbReleaseTask.setCompleteState(completeState.value);
        }
        iReleaseTaskService.update(tbReleaseTask);
    }

    private Long createReleaseTask(TbContainerConfig tbContainerConfig, Long deployTaskId, List<String> hostGroupIds) {
        TbReleaseTask tbReleaseTask = new TbReleaseTask();
        tbReleaseTask.setStageId(tbContainerConfig.getStageId());
        tbReleaseTask.setEnvironmentId(tbContainerConfig.getEnvironmentId());
        tbReleaseTask.setContainerConfigId(tbContainerConfig.getId());
        tbReleaseTask.setDeployInstallTaskId(deployTaskId);
        tbReleaseTask.setHostGroupIds(StringUtils.join(hostGroupIds, ConsoleConstant.HOST_GROUP_SEPARATOR));
        tbReleaseTask.setExecuteState(TbReleaseTaskEnum.EXECUTE_STATE.INIT.code);
        tbReleaseTask.setCompleteState(TbPublicEnum.TASK_COMPLETE_STATE.INIT.value);
        tbReleaseTask.setGmtIsDeleted(TbPublicEnum.GMD_IS_DEL.NO.code);
        //
        ShiroToken.User currentUser = iSSOService.getCurrentUser();
        tbReleaseTask.setUserId(currentUser.getId());
        tbReleaseTask.setUserName(currentUser.getName());
        //
        iReleaseTaskService.save(tbReleaseTask);
        return tbReleaseTask.getId();
    }
}
